#ifndef __WIZ_GEOMETRY_GEOMETRY_PLANE_HPP__SHANHAOBO_19800429__
#define __WIZ_GEOMETRY_GEOMETRY_PLANE_HPP__SHANHAOBO_19800429__

#include "../../WizAlgebra/Include/Utils/WizAlgebraUtils.hpp"

#include "./WizGeometryEnums.hpp"

namespace Wiz
{
    namespace Plane
    {
        template<typename ElementT>
        struct Type
        {
            WIZ_DECLARE_CLASS_THIS(Type);
            ////////////////////////////////////////////////////////////////
            typedef ElementT                            tElement;
            typedef ::Wiz::Vector3::Type<tElement>      tVector3;
            typedef ::Wiz::Vector3::Type<tElement>      tOrientation3;
            typedef ::Wiz::Vector3::Type<tElement>      tLocation3;

            ////////////////////////////////////////////////////////////////
            tOrientation3          m_Normal;
            tElement               m_Constant;///The distance of the origin to the plane
            ////////////////////////////////////////////////////////////////
            Type()
            {

            }

            /// constant:The distance of the origin to the plane
            Type(const tOrientation3& normal, const tElement& constant) : m_Normal(normal), m_Constant(constant)
            {
                WIZ_ASSERT(m_Constant > 0);
            }

            /// point:Point on the plane
            /// Important:The DOT PRODUCT of the normal of the Plane and any point on the Plane is Equal to the distance of the origin to the plane 
            Type(const tOrientation3& normal, const tLocation3& point) : m_Normal(normal), m_Constant(tVector3::Dot(this->m_Normal, point))
            {
                WIZ_ASSERT(m_Constant > 0);
            }

            Type(const tLocation3& p1, const tLocation3& p2, const tLocation3& p3)
            {
                const tVector3 k1 = p2 - p1;
                const tVector3 k2 = p3 - p2;
                this->m_Normal   = tOrientation3::Cross(k1 , k2);
                this->m_Normal.Normalize();
                this->m_Constant = this->m_Normal.Dot(p1);

                WIZ_ASSERT(m_Constant > 0);
            }

        public:
            tOrientation3& GetNormal()
            {
                return this->m_Normal;
            }

            const tOrientation3& GetNormal() const
            {
                return this->m_Normal;
            }

            tElement GetConstant()
            {
                return this->m_Constant;
            }

            const tElement GetConstant() const
            {
                return this->m_Constant;
            }

            WIZ_INLINE tElement Distance(const tVector3& p) const
            {
                return tVector3::Dot(this->m_Normal, p) - this->m_Constant;
            }

            WIZ_INLINE tElement Normalize()
            {
                tElement fLength = this->m_Normal.Length();

                // Will also work for zero-sized vectors, but will change nothing
                if (::Wiz::GreaterZero(fLength))
                {
                    const tElement fInvLength = 1 / fLength;
                    this->m_Normal                  *= fInvLength;
                    this->m_Constant                *= fInvLength;
                }

                return fLength;
            }

            //--------------------------------------------------------------
            ::Wiz::Side::Type GetSide (const tVector3& Pnt) const
            {
                const tElement fDistance = Distance(Pnt);

                if (::Wiz::LessZero(fDistance))
                {
                    return ::Wiz::Side::eNegative;
                }

                if (::Wiz::GreaterZero(fDistance))
                {
                    return ::Wiz::Side::ePositive;
                }

                return ::Wiz::Side::eNo;
            }

            ::Wiz::Bool::Type IsInPlane(const tVector3& Pnt) const
            {
                return ::Wiz::IsZero(this->Distance(Pnt));
            }

            //--------------------------------------------------------------
            ::Wiz::Side::Type GetSide (const tVector3& Pnt, const tVector3& HalfSize) const
            {
                tElement fDistance = Distance(Pnt);

                tElement maxAbsDist = tVector3::AbsDot(this->m_Normal, HalfSize);

                if (::Wiz::Less(fDistance, -maxAbsDist))
                {
                    return ::Wiz::Side::eNegative;
                }

                if (::Wiz::Greater(fDistance, maxAbsDist))
                {
                    return ::Wiz::Side::ePositive;
                }

                return ::Wiz::Side::eBoth;
            }

            ////////////////////////////////////////////////////////////////
            WIZ_INLINE friend ::Wiz::Bool::Type operator==(tThisIn p1, tThisIn p2)
            {
                return (::Wiz::IsEqual(p1.m_Constant, p1.m_Constant) && (p2.m_Normal == p2.m_Normal));
            }
            WIZ_INLINE friend ::Wiz::Bool::Type operator!=(tThisIn p1, tThisIn p2)
            {
                return !this->operator==(p1, p2);
            }
        }; /// end of struct Type
    } /// end of namespace Plane
} /// end of namespace Wiz

#endif /*__WIZ_GEOMETRY_GEOMETRY_PLANE_HPP__SHANHAOBO_19800429__*/
